package controllers;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

import org.openid4java.OpenIDException;
import org.openid4java.consumer.InMemoryConsumerAssociationStore;
import org.openid4java.discovery.DiscoveryInformation;
import org.openid4java.message.AuthRequest;
import org.openid4java.message.ParameterList;

import play.Play;
import play.cache.Cache;
import play.exceptions.UnexpectedException;
import play.libs.OpenID.UserInfo;
import play.libs.WS.WSRequest;
import play.modules.market.MarketProfile;
import play.mvc.Controller;
import play.mvc.Http.Request;
import utils.api.service.MarketUserService;
import utils.openid.ConsumerFactory;

import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import com.google.step2.AuthRequestHelper;
import com.google.step2.AuthResponseHelper;
import com.google.step2.ConsumerHelper;
import com.google.step2.Step2;
import com.google.step2.discovery.IdpIdentifier;
import com.google.step2.openid.ui.UiMessageRequest;

import exceptions.MarketConfigurationException;

public class MarketController extends Controller {

	public static void login() {
		// Check if we come here from another action like setup action in application controller.
		if(flash.get("from") != null) {
			request.params.put("from", flash.get("from"));
			request.params.put("domain", flash.get("domain"));
			request.params.put("redirect", flash.get("redirect"));
			session.put("test.redirect", flash.get("redirect"));
		} 
		
		String from = request.params.get("from");
		String domain = request.params.get("domain");
		String redirect = session.get("test.redirect");
		
		if("google".equals(from)) domain = request.params.get("domain");
		else domain = request.params.get("hd");
		if (domain != null) {
			// User attempting to login with provided domain, build and OpenID request and redirect
			try {
				session.put("user.domain", domain);
				ConsumerFactory factory = new ConsumerFactory(new InMemoryConsumerAssociationStore());
				ConsumerHelper consumerHelper = factory.getConsumerHelper();
				String realm = Play.configuration.getProperty("realm", request.getBase());
				String returnToUrl = request.getBase()+request.path;
				AuthRequest authRequest = startAuthentication(domain, realm, returnToUrl);
				String url = authRequest.getDestinationUrl(true);
				redirect(url);
			} catch (OpenIDException e) {
				// throw new Exception("Error initializing OpenID request", e);
				e.printStackTrace();
				session.put("user.domain", null);
			}
		} else {
			// This is a response from the provider, go ahead and validate
			try {
				MarketProfile userProfile = completeAuthentication(request);
				userProfile.setDomain(session.get("user.domain"));
				// Provisioning API for getting more information about user.
				MarketUserService userService = new MarketUserService(userProfile.getEmail(), userProfile.getDomain());
				userProfile.setAdmin(userService.isAdmin(userProfile.getUserName()));
				
				Class model = Class.forName(getModel());
                Method method = model.getMethod("findForMarketOAuth", new Class[] { MarketProfile.class });
                if(Modifier.isStatic(method.getModifiers())){
                    method.invoke(null, userProfile);
                }else{
                    throw new UnexpectedException("Module m4fcity expects your findForMarketOAuth method to be static");
                }
				/*session.put("user", user.getEmail());
				session.put("user.id", user.getClaimedId());
				session.put("user.firstName", user.getFirstName());
				session.put("user.lastName", user.getLastName());
				session.put("user.email", user.getEmail());
				//request.getSession().setAttribute("user", user);*/
                if(redirect != null && !("").equals(redirect)) {
                	session.put("test.redirect", null);
                	redirect(redirect);
                } else {
                	redirect(getLandUrl());
                }
			} catch(ClassNotFoundException e){
				throw new UnexpectedException("Module m4fcity cannot find your model class "+getModel());
			} catch(NoSuchMethodException e){
				throw new UnexpectedException("Module m4fcity requires that your model class "+getModel()+" must provide a method with this signature: [public static void findForM4FCityOAuth(M4FCityProfile data)]");
			} catch(IllegalAccessException e){
				throw new UnexpectedException("Module m4fcity does not have access to call your model's findForM4FCityOAuth");
			} catch(InvocationTargetException e){
				throw new UnexpectedException("Module m4fcity encountered an error while calling your model's findForM4FCityOAuth: "+e.getMessage());
			} catch (OpenIDException e) {
				//throw new Exception("Error processing OpenID response", e);
				e.printStackTrace();
				session.put("user.domain", null);
			}
		}
	}
	
	static AuthRequest startAuthentication(String op, String realm, String returnToUrl) throws OpenIDException {
		IdpIdentifier openId = new IdpIdentifier(op);
			
		//String realm = realm(request);
		//String returnToUrl = returnTo(request);
		ConsumerFactory factory = new ConsumerFactory(new InMemoryConsumerAssociationStore());
 	    ConsumerHelper consumerHelper = factory.getConsumerHelper(); 
 	    Cache.add("openId", openId);
 	    Cache.add("returnToUrl", returnToUrl);
		AuthRequestHelper helper = consumerHelper.getAuthRequestHelper(openId, returnToUrl);
		addAttributes(helper);
		
		//HttpSession session = request.getSession();
		AuthRequest authReq = helper.generateRequest();
		authReq.setRealm(realm);
		
		UiMessageRequest uiExtension = new UiMessageRequest();
		uiExtension.setIconRequest(true);
		authReq.addExtension(uiExtension);
		
		//session.pùt("discovered", helper.getDiscoveryInformation());
		Cache.add("discovered", helper.getDiscoveryInformation());
		return authReq;
	}
	   
	static void addAttributes(AuthRequestHelper helper) {
		helper.requestAxAttribute(Step2.AxSchema.EMAIL, true)
		.requestAxAttribute(Step2.AxSchema.FIRST_NAME, true)
		.requestAxAttribute(Step2.AxSchema.LAST_NAME, true);
	}
	   
	static MarketProfile completeAuthentication(Request request) throws OpenIDException {
		//HttpSession session = request.getSession();
		ConsumerFactory factory = new ConsumerFactory(new InMemoryConsumerAssociationStore());
		ConsumerHelper consumerHelper = factory.getConsumerHelper();
		IdpIdentifier openId = (IdpIdentifier) Cache.get("openId");
		String returnToUrl = (String) Cache.get("returnToUrl");
		AuthRequestHelper helper = consumerHelper.getAuthRequestHelper(openId, returnToUrl);
		ParameterList openidResp = getParameterList(request);
		String receivingUrl = getUrlWithQueryString(request);
		DiscoveryInformation discovered = (DiscoveryInformation) Cache.get("discovered");
		AuthResponseHelper authResponse = consumerHelper.verify(receivingUrl, openidResp, discovered);
		if (authResponse.getAuthResultType() == AuthResponseHelper.ResultType.AUTH_SUCCESS) {
			return onSuccess(authResponse);
		}
		return onFail(authResponse);
	}
	   
	static MarketProfile onSuccess(AuthResponseHelper helper) {
		return new MarketProfile(helper.getClaimedId().toString(), 
				helper.getAxFetchAttributeValue(Step2.AxSchema.EMAIL),
				helper.getAxFetchAttributeValue(Step2.AxSchema.FIRST_NAME),
				helper.getAxFetchAttributeValue(Step2.AxSchema.LAST_NAME));
	}
	   
	static MarketProfile onFail(AuthResponseHelper helper) {
		return null;
	}
	   
	public static String getUrlWithQueryString(Request req) {
		StringBuffer receivingUrl = new StringBuffer(request.getBase()+req.url);
		String queryString = req.querystring;
		if (queryString != null && queryString.length() > 0) {
			receivingUrl.append("?").append(req.querystring);
	    }
		//  log.info(receivingUrl.toString());
		return receivingUrl.toString();
	}

	/**
	* Returns a ParameterList (list of openid-related query parameters) from
	* an HttpServletRequest.
	* @param req the HttpServletRequest
	* @return a ParameterList
	*/
	public static ParameterList getParameterList(Request req) {
		return new ParameterList(params.all());
	}
	
	/**
	 * Gets the model.
	 * 
	 * @return the model
	 */
	public static String getModel() {
		return Play.configuration.getProperty("market.model", "model.user");
	}
	
	/**
	 * Gets the land url.
	 * 
	 * @return the land url
	 */
	public static String getLandUrl() {
		return Play.configuration.getProperty("market.landUrl", "/");
	}
}
